package com.ruyiadmin.springcloud.producer.controller.system;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruyiadmin.springcloud.producer.common.annotations.system.Log;
import com.ruyiadmin.springcloud.producer.common.annotations.system.Permission;
import com.ruyiadmin.springcloud.producer.common.beans.system.DirectoryConfig;
import com.ruyiadmin.springcloud.producer.common.beans.system.LogConfig;
import com.ruyiadmin.springcloud.producer.common.core.business.enums.OperationType;
import com.ruyiadmin.springcloud.producer.common.core.system.entities.QueryCondition;
import com.ruyiadmin.springcloud.producer.common.core.system.entities.QueryResult;
import com.ruyiadmin.springcloud.producer.common.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springcloud.producer.domain.entity.system.SysEsLog;
import com.ruyiadmin.springcloud.producer.domain.entity.system.SysLog;
import com.ruyiadmin.springcloud.producer.service.iservices.system.ISysLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.modelmapper.ModelMapper;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 * 审计日志表 前端控制器
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-12
 */
@RestController
@RequestMapping("/LogManagement")
@Api(tags = "系统审计日志管理服务")
@RequiredArgsConstructor
@Slf4j
public class SysLogController {

    //region 服务私有属性

    private final ISysLogService logService;
    private final MongoTemplate mongoTemplate;
    private final LogConfig logConfig;
    private final DirectoryConfig directoryConfig;
    private final ElasticsearchRestTemplate restTemplate;
    private final ModelMapper modelMapper;

    //endregion

    //region 查询日志列表

    @PostMapping("/Post")
    @ApiOperation(value = "查询日志列表")
    @Log(OperationType = OperationType.QueryList)
    @Permission(permission = "log:query:list")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public QueryResult<SysLog> queryByPage(@RequestBody QueryCondition queryCondition)
            throws ExecutionException, InterruptedException {
        CompletableFuture<QueryResult<SysLog>> future = CompletableFuture.supplyAsync(() -> {
            if (this.logConfig.isSupportMongoDB()) {

                //region 从MongoDB查询日志信息

                Query query = queryCondition.toCriteriaQuery();
                //查询总数
                long total = this.mongoTemplate.count(query, SysLog.class);
                //转化分页条件
                query.with(PageRequest.of(queryCondition.getPageIndex(), queryCondition.getPageSize()));
                //查询记录
                List<SysLog> records = this.mongoTemplate.find(query, SysLog.class);
                //反馈结果
                return QueryResult.success(total, records);

                //endregion

            } else if (this.logConfig.isSupportElasticsearch()) {

                //region 从Elasticsearch查询日志信息

                long total = 0;
                List<SysLog> records = new ArrayList<>();

                NativeSearchQuery nativeSearchQuery = queryCondition.toNativeSearchQuery();
                SearchHits<SysEsLog> searchHits = restTemplate.search(nativeSearchQuery, SysEsLog.class);

                total = searchHits.getTotalHits();

                List<SysEsLog> list = new ArrayList<>();
                for (SearchHit<SysEsLog> hit : searchHits.getSearchHits()) {
                    list.add(hit.getContent());
                }

                list.forEach(item -> {
                    SysLog sysLog = this.modelMapper.map(item, SysLog.class);
                    sysLog.setCreateTime(item.getCreateTime()
                            .toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDateTime());
                    sysLog.setModifyTime(item.getModifyTime()
                            .toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDateTime());
                    records.add(sysLog);
                });

                return QueryResult.success(total, records);

                //endregion

            } else if (this.logConfig.isSupportMeilisearch()) {

                //region 从Meilisearch查询日志信息

                long total = 0;
                List<SysLog> records = new ArrayList<>();
                return QueryResult.success(total, records);

                //endregion

            } else {

                //region 从关系库查询日志信息

                QueryWrapper<SysLog> wrapper = new QueryWrapper<>();//设置条件
                queryCondition.getQueryWrapper(wrapper);//转化查询条件、转化排序条件
                Page<SysLog> page = new Page<>(queryCondition.getPageIndex(), queryCondition.getPageSize());//初始化page

                this.logService.page(page, wrapper);//执行查询
                long total = page.getTotal();//总数
                List<SysLog> records = page.getRecords();//结果

                return QueryResult.success(total, records);

                //endregion

            }
        });
        return future.get();
    }

    //endregion

    //region 获取日志明细

//    @GetMapping("/GetById/{logId}")
//    @ApiOperation(value = "获取日志明细")
//    @Log(OperationType = OperationType.QueryEntity)
//    @Permission(permission = "log:query:entity")
//    public ActionResult getById(@PathVariable("logId") String logId) {
//        return ActionResult.success(this.logService.getById(logId));
//    }

    //endregion

    //region 下载返回数据

    @GetMapping("/DownloadMonitoringLog/{txtId}")
    @ApiOperation(value = "下载返回数据")
    @Log(OperationType = OperationType.DownloadFile)
    public void downloadMonitoringLog(@PathVariable("txtId") String txtId, HttpServletResponse response) {
        FileInputStream fis = null;
        ServletOutputStream sos = null;
        try {
            String fileName = txtId + ".txt";
            String filePath = this.directoryConfig.getMonitoringLogsPath() + "\\" + fileName;
            log.info(filePath);
            response.setHeader("Content-Disposition", "attachment;filename="
                    + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            fis = new FileInputStream(FileUtil.file(filePath));
            sos = response.getOutputStream();
            IOUtils.copy(fis, sos);
        } catch (Exception e) {
            throw new RuYiAdminCustomException("download error");
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (sos != null) {
                    sos.flush();
                    sos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //endregion

}
